package semantic;

import java.util.*;

public class Semantic {
    private Map<Sentence, Double> distance = new HashMap<>();
    List<Map.Entry<Sentence, Double>> predictions;

    private static final int K = 4;

    /**
     * Stores the sentence and its corresponding meaning.
     * True represents yes, and false represents no.
     */
    private static HashMap<Sentence, Boolean> answer = new HashMap<>();

    // Store the occurrences of all feature words.
    private static ArrayList<String> allWords = new ArrayList<>();

    // The sentences representing yes.
    private static final String[] yesList = {"yes", "sure", "of course",
            "yes, i love you", "yes, i like you"};

    // The sentences representing no.
    private static final String[] noList = {"excuse me", "no", "not at all",
            "no, i hate you", "no, i dislike you", "no, i don't like you",
            "no, i do not like you"};

    // the number of answer sentences.
    private static int count = yesList.length + noList.length;

    /**
     * calculate answer sentences' vectors.
     */
    public Semantic() {
        initAnswerList(yesList, true);
        initAnswerList(noList, false);

        /**
         * Calculate the vector must after the init.
         */
        Iterator iter = answer.entrySet().iterator();
        while (iter.hasNext()) {
            HashMap.Entry entry = (HashMap.Entry) iter.next();
            Sentence sentence = (Sentence) entry.getKey();
            calVector(sentence);
        }
    }

    /**
     * Turn String to Sentence and calculate its vector.
     * Store all feature words in the answer list.
     *
     * @param list the list to be processed
     * @param flag the kind of the list to be processed
     */
    public void initAnswerList(String[] list, boolean flag) {
        for (int i = 0; i < list.length; ++i) {
            Sentence sentence = new Sentence(list[i]);
            HashMap<String, Integer> words = sentence.getWords();
            for (String word : words.keySet()) {
                if (!allWords.contains(word)) {
                    allWords.add(word);
                }
            }
            answer.put(sentence, flag);
        }
    }

    /**
     * Calculate the number of occurrence of the word in answer list.
     */
    public int occurrenceNum(String word) {
        int ret = 0;
        Iterator iter = answer.entrySet().iterator();
        while (iter.hasNext()) {
            HashMap.Entry entry = (HashMap.Entry) iter.next();
            Sentence sentence = (Sentence) entry.getKey();
            if (sentence.getWords().containsKey(word)) {
                ret++;
            }
        }
        return ret;
    }

    /**
     * Calculate the vector of the sentence.
     */
    public void calVector(Sentence sentence) {
        HashMap<String, Double> vector = new HashMap<>();
        HashMap<String, Integer> words = sentence.getWords();

        for (int i = 0; i < allWords.size(); ++i) {
            String word = allWords.get(i);
            if (words.containsKey(word)) {
                double tf = (double) words.get(word) / words.size();
                double idf = Math.log((double) count / (1 + occurrenceNum(word)));
                vector.put(word, tf * idf);
            } else {
                vector.put(word, 0.0);
            }
        }
        sentence.setVector(vector);
    }

    /**
     * Calculate the distance between the message and the answer in answer list.
     *
     * @param sentence the message that needs to be analyzed
     */
    public void calDistance(Sentence sentence) {
        distance.clear();
        calVector(sentence);
        HashMap<String, Double> v1 = sentence.getVector();

        Iterator iter = answer.entrySet().iterator();
        while (iter.hasNext()) {
            HashMap.Entry entry = (HashMap.Entry) iter.next();
            Sentence ansSentence = (Sentence) entry.getKey();
            HashMap<String, Double> v2 = ansSentence.getVector();

            double numerator = 0;
            double dominator1 = 0;
            double dominator2 = 0;
            for (String word : allWords) {
                numerator += v1.get(word) * v2.get(word);
                dominator1 += v1.get(word) * v1.get(word);
                dominator2 += v2.get(word) * v2.get(word);
            }
            dominator1 = Math.sqrt(dominator1 * dominator2);
            double sim = numerator / dominator1;
            distance.put(ansSentence, sim);
        }
    }

    public void sortDistance() {
        predictions = new ArrayList<>(distance.entrySet());
        Collections.sort(predictions, (o1, o2) -> o2.getValue().compareTo(o1.getValue()));
    }

    public Boolean getPrediction(Sentence sentence) {
        calDistance(sentence);
        sortDistance();

        /**
         * In each of the k neighbors of the test text,
         * calculate the weights of each class in turn.
         */
        double yesWeight = 0;
        double noWeight = 0;
        for (int i = 0; i < K; ++i) {
            if (answer.get(predictions.get(i).getKey())) {
                yesWeight += predictions.get(i).getValue();
            } else {
                noWeight += predictions.get(i).getValue();
            }
        }

        /**
         * Compare the weights of the two classes and divide the text
         * into the category with the largest weight.
         */
        return yesWeight > noWeight;
    }

    // For test
    public Boolean getPrediction(Sentence sentence, int K) {
        calDistance(sentence);
        sortDistance();

        /**
         * In each of the k neighbors of the test text,
         * calculate the weights of each class in turn.
         */
        double yesWeight = 0;
        double noWeight = 0;
        for (int i = 0; i < K; ++i) {
            if (answer.get(predictions.get(i).getKey())) {
                yesWeight += predictions.get(i).getValue();
            } else {
                noWeight += predictions.get(i).getValue();
            }
        }

        /**
         * Compare the weights of the two classes and divide the text
         * into the category with the largest weight.
         */
        return yesWeight > noWeight;
    }

//    public static int getCount() {
//        return count;
//    }

//    /**
//     * Test which K's correct rate is the biggest.
//     */
//    public static void main(String[] args) {
//        Semantic semantic = new Semantic();
//        String[] yesList = {"yes,i like u. ","i love you very much.", "i like you very much."};
//        String[] noList = {"i hate you", "i dislike you", "i don't like you",
//                "i do not like you"};
//
//        int total = yesList.length + noList.length;
//
//        for (int k = 1; k <= Semantic.getCount(); ++k) {
//            int right = 0;
//
//            for (int i = 0; i < yesList.length; ++i) {
//                if (semantic.getPrediction(new Sentence(yesList[i]), k)) {
//                    right++;
//                }
//            }
//
//            for (int i = 0; i < noList.length; ++i) {
//                if (!semantic.getPrediction(new Sentence(noList[i]), k)) {
//                    right++;
//                }
//            }
//            double rate = (double) right / total;
//            System.out.println("k = " + k);
//            System.out.println("Correct rate: " + rate);
//            System.out.println("----------------------------");
//        }
//    }
}
